/*
 * File: dlist.c
 * Author:  Yin Lijun <yinlijun2004@gmail.com>
 * Brief:   sys_config parser.
 *
 * Copyright (c) 2011 - 2012  Yin Lijun <yinlijun2004@gmail.com>
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * History:
 * ================================================================
 * 2013-11-15 Yin Lijun <yinlijun2004@gmail.com> created
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dlist.h"
#include "sys_cfg_tok.h"
#include "sys_cfg.h"

#define KEY_VALUE_MAX_LENGTH 64
#define CFG_LINE_MAX_LENGTH 512

enum TypeLine {
    TYPE_LINE_UNKNOWN = 0,
    TYPE_LINE_COMMNET,
    TYPE_LINE_MAIN_KEY,
    TYPE_LINE_KV,
    TYPE_LINE_BLANK 
};

struct CfgLine{
    enum TypeLine line_type;
    int line_number;
    char* orig_line;
    void* data;
};

void usage()
{
    printf("usage: ./gen_sys_cfg <file name>\n");
}

void cfg_line_print(void* ctx, struct CfgLine* cfg)
{
    if(cfg != NULL)
    {
        switch(cfg->line_type)
        {
            case TYPE_LINE_BLANK:
            case TYPE_LINE_COMMNET:
            case TYPE_LINE_KV:
            case TYPE_LINE_MAIN_KEY:
            case TYPE_LINE_UNKNOWN:
            default:
                printf("read line:%d\n%s\n", cfg->line_number, cfg->orig_line);
                break;
        }
    }
}

int cfg_line_cmp_by_line_number(void* num, void* data)
{
    int line_number = (int) num;
    struct CfgLine* cfg = (struct CfgLine *)data;
    return line_number == cfg->line_number;
}

Ret cfg_line_delete_by_line_number(DList* cfg_line, int line_number)
{
    int index = 0;
    dlist_find_by_data(cfg_line, (void *)line_number, cfg_line_cmp_by_line_number, &index);
    return dlist_delete(cfg_line, index);
}

struct CfgLine* cfg_line_create_from_str(char* line, int line_num)
{
    struct CfgLine* cfg = NULL;
    char* l = line;
    char* cursor = NULL;
    char* value = NULL;
    if(line != NULL)
    {
        do{
            cfg = (struct CfgLine *)calloc(1, sizeof(struct CfgLine));
            if(cfg == NULL)
            {
                perror("calloc CfgLine failed!\n");
                break;
            }
            cfg->line_type = TYPE_LINE_UNKNOWN;
            cfg->data = NULL;
            cfg_tok_nextstr(&l, &cursor);
            if(cursor[0] == 0x0
                    || cursor[0] == 0xd //carriage return
                    || cursor[0] == 0x20) //Space
            {
                cfg->line_type = TYPE_LINE_BLANK;
            }
            else if(cursor[0] == ';')
            {
                cfg->line_type = TYPE_LINE_COMMNET;
            }
            else if(cursor[0] == '[')
            {
                cfg->line_type = TYPE_LINE_MAIN_KEY;
            }
            else if((value = strchr(cursor, '=')) != NULL)
            {
                cfg->line_type = TYPE_LINE_KV;
            }
            cfg->line_number = line_num;
            cfg->orig_line = strdup(line);
            break;
        }while(0);
    }

    return cfg;
}

void cfg_line_destroy(void *data)
{
    struct CfgLine* cfg = (struct CfgLine *)data;
    if(cfg != NULL && cfg->orig_line != NULL)
    {
        free(cfg->orig_line);
    }
    free(cfg);
    cfg = NULL;
}

Ret sys_cfg_line_insert(struct SysConfig* sys_config, struct CfgLine* cfg)
{
    struct SubKey* sk = NULL;
    struct MainKey* mk = NULL;
    if(sys_config != NULL && cfg != NULL)
    {
        switch(cfg->line_type)
        {
            case TYPE_LINE_KV:
                sk = sys_cfg_sub_key_create_from_str(cfg->line_number, cfg->orig_line);
                return_if_fail_val(sk != NULL, RET_ERROR);
                sys_cfg_append_sub_key(sys_config, sk);
                cfg->data = (void *)sk;
                break;
            case TYPE_LINE_MAIN_KEY:
                mk = sys_cfg_main_key_create_from_str(cfg->line_number, cfg->orig_line);
                return_if_fail_val(mk != NULL, RET_ERROR);
                sys_cfg_append_main_key(sys_config, mk);
                cfg->data = (void *)mk;
                break;
            case TYPE_LINE_BLANK:
            case TYPE_LINE_COMMNET:
            case TYPE_LINE_UNKNOWN:
            default:
                break;
        }
    }
    return RET_OK;
}

void main_key_patch(void* ctx, void* data)
{
    return_if_fail(ctx != NULL && data != NULL);
    struct SubKey* sk = (struct SubKey *)data;
    //printf("%s key:%s value:%s\n", __func__, sys_cfg_sub_key_get_key(sk), sys_cfg_sub_key_get_value(sk));
    printf("%s key:%s value:%s\n", __func__, sys_cfg_sub_key_get_key(sk), sys_cfg_sub_key_get_value(sk));
    sys_cfg_patch_main_key((struct SysConfig *)ctx, sys_cfg_sub_key_get_key(sk), sys_cfg_sub_key_get_value(sk)); 
    //sys_cfg_delete_main_key((struct SysConfig *)ctx, sys_cfg_sub_key_get_value(sk));
}

Ret sys_cfg_patch_config_info(struct SysConfig* thiz, const char* info_str)
{
    struct MainKey* config_info = sys_cfg_get_main_key(thiz, info_str);
    //printf("%s %d main key:%p\n", __func__, __LINE__, config_info);
    //DList* sub_key = sys_cfg_get_sub_key_list(config_info);
    //printf("%s %d sub key:%p\n", __func__, __LINE__, sub_key);
    dlist_foreach(sys_cfg_get_sub_key_list(config_info), main_key_patch, (void *)thiz);
    //return sys_cfg_delete_main_key(thiz, info_str);
    return RET_OK;
}

int main(int argc, char**argv)
{
    if(argc != 2 && argv[1] == NULL)
    {
        usage();
        return 0;
    }

    FILE* src_file = fopen(argv[1], "r");
    if(!src_file)
    {
        perror("open source file failed\n");
        return -1;
    }

    int line_num = 0;
    char line[CFG_LINE_MAX_LENGTH] = {0};
    char *new_line = line;

    struct SysConfig *sys_config = sys_cfg_create();
    return_if_fail_val(sys_config != NULL, -1);
    DList* cfg_line = dlist_create(cfg_line_destroy);

    while((new_line = fgets(new_line, CFG_LINE_MAX_LENGTH, src_file)) != NULL)
    {
        struct CfgLine* line = cfg_line_create_from_str(new_line, line_num);
        if(line != NULL)
        {
            dlist_append(cfg_line, line);
            //cfg_line_print(NULL, line);
            sys_cfg_line_insert(sys_config, line); 
        }
        line_num++;
        memset(new_line, 0, CFG_LINE_MAX_LENGTH);
    }


    //dlist_foreach(cfg_line, cfg_line_print, NULL);
    //sys_cfg_dump(sys_config, NULL); 
    sys_cfg_patch_config_info(sys_config, "config_info");
    sys_cfg_dump(sys_config, NULL); 
    printf("%s, %d\n", __func__, __LINE__);
    dlist_destroy(cfg_line);
    printf("%s, %d\n", __func__, __LINE__);
    sys_cfg_destroy(sys_config);
    printf("%s, %d\n", __func__, __LINE__);
    fclose(src_file);
    printf("%s, %d\n", __func__, __LINE__);

    return 0;
}
